"""Classes for managing Defence campaigns"""
import os.path
from ConfigParser import RawConfigParser, NoOptionError
from collections import defaultdict

import pygame

from settings import settings
from graphics import request_replace
import events

import gui
import parse
import levels

class MapScreen(object):
	"""A campaigns map screen displaying a map and all visible linked nodes"""
	
	def __init__(self, campaign, menu):
		self.menu = menu
		self.campaign = campaign
		
		self.title = settings.fonts.title.render(self.campaign.name, True, 
			(255, 255, 255), (0, 0, 0))
		
		self.map_graphic = os.path.join(settings.paths.campaigns, 
			self.campaign.directory, "map.png")
		request_replace(self, attr="map_graphic", relative=False)
		
		self.rect = pygame.Rect((0, 0, 640, 480))
		
		self.current = set()
		
		map_height = self.map_graphic.get_height()
		self.offset = (0, 272 - (map_height // 2))
		
		#self.back = BackButton(self.menu, position=(510, 315))
		
		self.nodes = self.campaign.nodes
		self.current |= self.nodes
		
		for node in self.nodes:
			node.menu = menu
			node.completed, node.carryover = self.campaign.levels[node.levelname]

	def update(self):
		"""Update the map screen"""
		pass
		
	def draw(self, surface):
		"""Draw the map screen to surface"""
		surface.fill((0, 0, 0))
		width = self.title.get_width()
		surface.blit(self.title, (320 - (width // 2), 0))
		
		map_width, map_height = self.map_graphic.get_size()
		
		surface.blit(self.map_graphic, (0, 272 - (map_height // 2)))
		
		map_surface = surface.subsurface(pygame.Rect(0, 272 - (map_height // 2),
			map_width, map_height))
		
		for node in self.campaign.start_nodes:
			node.draw_visible(map_surface)
		
		#self.back.draw(surface)
		
	def event(self, event):
		"""Handle events"""
		if event.type == pygame.MOUSEBUTTONDOWN:
			if self.rect.collidepoint(event.pos):
				x, y = event.pos
				off_x, off_y = self.offset
				pos = (x - off_x, y - off_y)
				
				event = pygame.event.Event(pygame.MOUSEBUTTONDOWN, pos=pos)
				
				for widget in self.current:
					widget.event(event)
					#self.back.event(event)

class MissionNode(gui.Button):
	@classmethod
	def path(cls, campaign, *node_tuples):
		nodes = [cls(campaign, *node_tuple) for node_tuple in node_tuples]
		
		for index, node in enumerate(nodes[:-1]):
			node.link(nodes[index + 1])
			
		return nodes
	
	def __init__(self, campaign, position, levelname):
		self.campaign = campaign
		self.position = position
		
		x, y = position
		self.rect = pygame.Rect((x - 2, y - 2, 4, 4))
		
		self.levelname = levelname
		
		self.children = set()
		
		self.completed = False
		self.carryover = []
	
	def link(self, node):
		self.children.add(node)
	
	def draw(self, surface):
		colour = (0, 192, 0) if self.completed else (192, 0, 0)
		
		pygame.draw.circle(surface, (0, 0, 0), self.position, 4)
		pygame.draw.circle(surface, colour, self.position, 3)
	
	def event(self, event):
		if event.type == pygame.MOUSEBUTTONDOWN:
			if self.rect.collidepoint(event.pos):
				#print self.levelname
				#self.completed = not self.completed
				
				if not self.campaign.progress.has_section(self.levelname):
					self.campaign.progress.add_section(self.levelname)
				
				#self.campaign.progress.set(self.levelname, "completed", 
				#self.completed)
				
				#self.campaign.save()
				self.action(event)
	
	def action(self, event):
		event = pygame.event.Event
		
		#level = self.campaign.level(self.levelname)
		
		pygame.event.post(event(events.LEVELCHANGE, campaign=self.campaign, level=self.levelname))
		pygame.event.post(event(events.MENU))
	
	def relatives(self, ignore=set()):
		relatives = set()
		
		for node in self.children:
			if node not in ignore:
				relatives.add(node)
				relatives |= node.relatives(ignore | relatives)
		
		return relatives
	
	def draw_links(self, surface):
		for child in self.children:
			pygame.draw.line(surface, (192, 192, 192), self.position,
				child.position)
	
	def draw_visible(self, surface):
		if self.completed:
			for child in self.children:
				pygame.draw.line(surface, (0, 0, 0), self.position,
					child.position)
				child.draw_visible(surface)
		
		self.draw(surface)

class Campaign(object):
	def __init__(self, path):
		filepath = os.path.join(path, "campaign.txt")
		campaignfile = open(filepath)
		
		self.directory = os.path.split(path)[1]
		self.map_screen = None
		
		self.nodes = set()
		self.start_nodes = set()
		
		details = {"name": "", "author": ""}
		
		def parse_levels(lines):
			"""Parse lines as a levels section of a campaign"""
			
			def link(path_a, path_b):
				"""Link the last node of path_a to the first node of path_b"""
				path_a[-1].link(path_b[0])
			
			def entry(path):
				"""Add the first node of path to the start nodes"""
				self.start_nodes.add(path[0])
			
			def path(*args, **kwargs):
				"""Create a path and add all the nodes in it to the nodes"""
				path = MissionNode.path(self, *args, **kwargs)
				self.nodes.update(path)
				
				return path
			
			object_types = {
				"path": path,
				#"final": paths.End.path,
				"start": entry,}
			
			parse.section(lines, object_types, operators={"->": link})
		
		parsers = {
			"details": parse.details(details),
			"levels": parse_levels}
		
		parser = parse.Parser(file=campaignfile, parsers=parsers)
		
		parser.section("details")
		parser.section("levels")
		
		self.name = details["name"]
		self.author = details["author"]
		
		progresspath = os.path.join(settings.paths.progress, 
			"%s.ini" % self.directory)
		
		self.progress = RawConfigParser()
		
		self.progress.read(progresspath)
		
		def default_level():
			"""Return an uncompleted, no carry-over level data"""
			return (False, [])
		
		self.levels = defaultdict(default_level)
		
		for level in self.progress.sections():
			try:
				completed = self.progress.getboolean(level, "completed")
			except NoOptionError:
				completed = False
			
			try:
				carryover = self.progress.get(level, "carryover")
			except NoOptionError:
				carryover = [] 
			else:
				carryover = carryover.strip("[]")
				carryover = [unit.strip() for unit in carryover.split(",") 
					if unit]
			
			self.levels[level] = (completed, carryover)
	
	def level(self, levelname, game):
		levelpath = os.path.join(settings.paths.campaigns, self.directory, 
			levelname)
		level = levels.Level(game, self, levelpath)
		
		return level
	
	def save(self, file_=None):
		if file_ is None:
			file_ = open(os.path.join(settings.paths.progress, 
				"%s.ini" % self.directory), 'w')
		
		self.progress.write(file_)
	
	def show_map(self, menu):
		self.map_screen = MapScreen(self, menu)
		
		return self.map_screen
